package com.sddfp.system.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sddfp.client.access.TradeLogApi;
import com.sddfp.common.constant.user.UserConstant;
import com.sddfp.common.exception.Assert;
import com.sddfp.common.pojo.*;
import com.sddfp.common.pojo.SignatureDto;
import com.sddfp.common.result.enums.impl.BusinessCode;
import com.sddfp.common.result.enums.impl.ResultCode;
import com.sddfp.common.utils.JwtTemplate;
import com.sddfp.common.utils.TokenUtils;
import com.sddfp.system.entity.User;
import com.sddfp.system.entity.UserCard;
import com.sddfp.system.entity.UserDetail;
import com.sddfp.system.mapper.UserCardMapper;
import com.sddfp.system.mapper.UserDetailMapper;
import com.sddfp.system.mapper.UserMapper;
import com.sddfp.system.service.IUserCardService;
import com.sddfp.system.service.IUserDetailService;
import com.sddfp.system.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author yellow doctor
 * @since 2025-06-23
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private UserMapper userMapper;


    @Autowired
    UserDetailMapper userDetailMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    JwtTemplate jwtTemplate;

    @Autowired
    IUserCardService userCardService;

    @Autowired
    UserCardMapper userCardMapper;

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    private TradeLogApi tradeLogApi;

    @Autowired
    IUserDetailService userDetailService;


    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    @Override
    public void register(UserRegisterDto userRegisterDto) {

        //查看电话是否重复
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class).eq(User::getPhone, userRegisterDto.getPhone());
        User user = userMapper.selectOne(wrapper);
        Assert.error(user!=null, BusinessCode.USER_EXISTED);

        User user1 = new User();
        user1.setPhone(userRegisterDto.getPhone());
        user1.setUserName(userRegisterDto.getUserName());
        String hashpw = BCrypt.hashpw(userRegisterDto.getPwd());
        user1.setPwd(hashpw);
        user1.setUserType(UserConstant.USER_TYPE_PERSONAL);
        user1.setUserStatus(false);
        userMapper.insert(user1);

        UserDetail userDetail = new UserDetail();
        userDetail.setUserName(userRegisterDto.getUserName());
        userDetail.setPhone(userRegisterDto.getPhone());
        userDetail.setBorrowMoney(BigDecimal.valueOf(0.0));
        userDetail.setId(user1.getId());
        userDetail.setUserCode("UC_"+UUID.randomUUID().toString().substring(0, 8));
        userDetail.setCardBindStatus(false);
        userDetailMapper.insert(userDetail);
    }

    @Override
    public LoginVo login(LoginDto loginDto ) {

     return loginDto.getIsPhoneLogin()?userPhoneLogin(loginDto):userPwdLogin(loginDto);

    }

    private LoginVo userPwdLogin(LoginDto loginDto) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
                .eq(User::getUserName, loginDto.getLoginName());
        User user = this.getOne(wrapper);
        Assert.error(user==null, BusinessCode.USER_NOT_EXIST);

        boolean b = BCrypt.checkpw(loginDto.getLoginSecret(), user.getPwd());
        Assert.error(!b,BusinessCode.LOGIN_FAIL);

        return getLoginVo(user);
    }

    private LoginVo userPhoneLogin(LoginDto loginDto) {

        // 校验验证码
        String key="user:code:"+loginDto.getLoginPhone();
        String code = stringRedisTemplate.opsForValue().get(key);
        Assert.error(!loginDto.getLoginCode().equals(code),BusinessCode.USER_VERIFY_CODE_ERROR);

        // 根据手机号码查询，如果手机号码不存在自动注册
        User user = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, loginDto.getLoginPhone()));

        if (user == null) {
            UserRegisterDto registerDto = new UserRegisterDto();
            // 手机验证码登录，手机号码不存在，自动注册，此时用户名随机生成、密码默认123456
            registerDto.setUserName(RandomUtil.randomString(6));
            registerDto.setPwd(BCrypt.hashpw("111111").toString());
            registerDto.setPhone(loginDto.getLoginPhone());

            // 保存用户、用户详情
            this.register(registerDto);

            // 根据手机号码查询
            user = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, loginDto.getLoginPhone()));
        }

        return getLoginVo(user);
    }


    private LoginVo getLoginVo(User user) {
        Map<String,Object> payload = new HashMap<>();
        payload.put("userId",user.getId());
        payload.put("userName",user.getUserName());
        payload.put("phone",user.getPhone());
        // 生成token
        String token = jwtTemplate.createToken(payload);

        log.info("当前用户是{}，token是{}",user.getUserName(),token);
        // 存储到Redis
        stringRedisTemplate.opsForValue().set(UserConstant.USER_TOKEN + user.getId(),token, Duration.ofMinutes(30));
        return LoginVo.builder()
                .id(user.getId())
                .userName(user.getUserName())
                .phone(user.getPhone())
                .token(token)
                .build();
    }


    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    @Override
    public SignatureDto addBankAccount(CardBindDto cardBindDto) {
        //1、从token中获取userId   Authorization Bearer xx
        Long userId = TokenUtils.getTokenUserId();

        //2、校验：根据用户id查询u_user_detail用户表，根据CARD_BIND_STATUS字段判断是否已经绑卡
        LambdaQueryWrapper<UserDetail> wrapper = Wrappers.lambdaQuery(UserDetail.class)
                .eq(UserDetail::getId, userId);
        UserDetail userDetail = userDetailMapper.selectOne(wrapper);
        Assert.error(userDetail!=null && userDetail.isCardBindStatus(), BusinessCode.USER_ALREADY_BINDED);

        //3先判断是否已经绑卡
        Long ucCount = userCardMapper.selectCount(Wrappers.lambdaQuery(UserCard.class)
                .eq(UserCard::getCardNo, cardBindDto.getCardNo()));
        Assert.error(ucCount!=null && ucCount > 0, BusinessCode.USER_CARD_ALREADY_BINDED);

        //3、如果未绑卡，保存保卡信息到u_user_card表
        UserCard userCard = new UserCard();
        userCard.setUserId(userDetail.getId());
        userCard.setPhone(userDetail.getPhone());
        userCard.setCardNo(cardBindDto.getCardNo());
        userCard.setStatus(true);
        userCardService.save(userCard);
        //4、更新用户表：把请求参数中的用户真实姓名、身份证号码更新到u_user表中
        //update set x1=v1,x2=v2 where id = xx
        update(Wrappers.lambdaUpdate(User.class)
                .set(User::getIdentityCode,cardBindDto.getIdentityCode())
                .set(User::getRealName,cardBindDto.getRealName())
                .eq(User::getId, userId)
        );
        //5、生成请求流水号，用来访问托管代理，并更新u_user_detail表中流水号字段REQ_CODE
        String reqCode = UUID.randomUUID().toString().replace("-","").substring(0, 10);
        userDetailMapper.update(null,Wrappers.lambdaUpdate(UserDetail.class)
                .set(UserDetail::getReqCode,reqCode)
                .eq(UserDetail::getId,userId)
        );
        //6、远程访问托管代理服务（暂不实现）
//        String url="http://localhost:9030/trade-log/bank/access/card";
//        BankCardAddDTO bankCardAddDTO = new BankCardAddDTO();
//        bankCardAddDTO.setUserId(userId);
//        bankCardAddDTO.setReqCode(reqCode);
//        ResponseEntity<SignatureDto> responseEntity = restTemplate.postForEntity(url, bankCardAddDTO, SignatureDto.class);
//        SignatureDto signatureDto = responseEntity.getBody();

        BankCardAddDTO bankCardAddDTO = new BankCardAddDTO();
        bankCardAddDTO.setUserId(userId);
        bankCardAddDTO.setReqCode(reqCode);
        SignatureDto signatureDto = tradeLogApi.proxyBindCardLog(bankCardAddDTO);
        return signatureDto;
    }


    @Override
    public UserInfoVo findById(Long userId) {
        // 返回字段分析：user表（userName,identityCode）
        // 返回字段分析：user_detail表（userCode）

        // 根据id查询数据库
        User user = this.getById(userId);
        // 自定义断言判断，减少if判断，让代码更简洁
        Assert.error(user == null, ResultCode.DATA_IS_WRONG);
        // 返回结果
        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setUserName(user.getUserName());
        userInfoVo.setIdentityCode(user.getIdentityCode());

        // 根据用户id查询用户编码； 用户与用户详情共用主键
        UserDetail userDetail = userDetailService.getById(userId);
        userInfoVo.setUserCode(userDetail.getUserCode());

        return userInfoVo;
    }
}
